﻿using BMC.ARSystem;
//------------------------------------------------------------------
// System Name:    NRemedy
// Component:      NRemedy
// Create by:      Parker Zhou (parkerz@wicresoft.com)
// Create Date:    2012-04-10
//------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Wicresoft.EKA.NRemedy
{

    /// <summary>
    /// ModelBinder is responsed bind the raw fieldvalueList into T
    /// and unbind the T down to fieldvaluelist
    /// </summary>
    /// <typeparam name="T">Model Type</typeparam>
    public class ModelBinder<T> : IModelBinder<T> where T : ARBaseForm
    {

        public FieldValueList UnBindForUpdate(T Model)
        {
            return UnBind(Model, new PropertyFilterDelegate(PropertyFilterForChanged));
        }

        public FieldValueList UnBind(T Model)
        {
            return UnBind(Model, null);
        }

        //与Update一致了，以前是上一个方法，导致无法保留和给Form 赋默认值。
        public FieldValueList UnBindForCreate(T Model)
        {
            return UnBind(Model, new PropertyFilterDelegate(PropertyFilterForChanged));
        }

        public FieldValueList UnBind(T Model, PropertyFilterDelegate filter)
        {
            if (Model == null) throw new ArgumentNullException("Model");
            FieldValueList list = new FieldValueList();

            ARFieldAttribute arField = null;
            object value = null;
            ARType type;

            foreach (PropertyInfoAndARAttribute propertyAndAR in BinderHelper.GetPropertyInfos(typeof(T), false))
            {

                arField = propertyAndAR.ARAttribute;

                if (Global.CoreFields.ContainsKey(arField.DatabaseID)) continue;

                //if filter supplied,check if being filter
                if (filter != null && filter(Model as ARBaseForm, propertyAndAR.Property) == false)
                    continue;

                value = propertyAndAR.FastInvoker.Execute(Model, null);
                type = arField.DataType;

                if (type == ARType.SelectionField && -1 == ((int)value))
                    value = null;

                list.Add(arField.DatabaseID, value, (int)type);
            }
            return list;
        }

        public T Bind(FieldValueList valueList)
        {
            if (valueList == null) throw new ArgumentNullException("valueList");

            T model = Activator.CreateInstance<T>();

            List<PropertyInfoAndARAttribute> properties = BinderHelper.GetPropertyInfos(typeof(T), true);

            PropertyInfoAndARAttribute propertyAndAR = null;
            ARFieldAttribute arField = null;
            object value;
            Type NETType;

            foreach (uint item in valueList.Keys)
            {
                propertyAndAR = properties.FirstOrDefault(o => o.ARAttribute.DatabaseID == item);
                if (propertyAndAR == null) continue;
                arField = propertyAndAR.ARAttribute;
                value = valueList[arField.DatabaseID];
                NETType = propertyAndAR.Property.PropertyType;
                if ((null == value || DBNull.Value == value))
                {
                    if (NETType.IsGenericType && NETType.GetGenericTypeDefinition() == typeof(Nullable<>)) value = null; //将属性改为Nullable是可行的。
                    if (ARType.SelectionField == arField.DataType) value = Enum.ToObject(NETType, -1);
                    continue;
                }

                if (propertyAndAR.Property.PropertyType == typeof(DateTime)) value = ((DateTime)value).ToLocalTime();

                propertyAndAR.FastInvoker.Execute(model,
                    new object[] { value });
            }
            model.ClearChange();
            return model;
        }

        public T BindWithIDS(FieldValueList valueList)
        {
            if (valueList == null) throw new ArgumentNullException("valueList");

            T model = Activator.CreateInstance<T>();

            List<PropertyInfoAndARAttribute> properties = BinderHelper.GetPropertyInfos(typeof(T), true);

            PropertyInfoAndARAttribute propertyAndAR = null;
            ARFieldAttribute arField = null;
            object value;
            Type NETType;

            foreach (uint item in valueList.Keys)
            {
                propertyAndAR = properties.FirstOrDefault(o => o.ARAttribute.DatabaseID == item);
                if (propertyAndAR == null)
                    throw new InvalidCastException(string.Format("valueList cannot cast to model : {0}", typeof(T).FullName),
                        new Exception(string.Format("DatabaseID : {0} in model : {1} cannot be found in valueList",
                            propertyAndAR.ARAttribute.DatabaseID, typeof(T).FullName)));
                arField = propertyAndAR.ARAttribute;
                value = valueList[arField.DatabaseID];
                NETType = propertyAndAR.Property.PropertyType;
                if ((null == value || DBNull.Value == value))
                {
                    if (NETType.IsGenericType && NETType.GetGenericTypeDefinition() == typeof(Nullable<>)) value = null; //将属性改为Nullable是可行的。
                    if (ARType.SelectionField == arField.DataType) value = Enum.ToObject(NETType, -1);
                    continue;
                }

                if (propertyAndAR.Property.PropertyType == typeof(DateTime)) value = ((DateTime)value).ToLocalTime();

                propertyAndAR.FastInvoker.Execute(model,
                    new object[] { value });
            }
            model.ClearChange();
            return model;
        }


        public List<T> BindList(EntryFieldValueList list)
        {
            if (list == null) throw new ArgumentNullException("valueList");


            List<T> listM = new List<T>();
            T model = default(T);
            FieldValueList fieldValue;

            List<PropertyInfoAndARAttribute> properties = BinderHelper.GetPropertyInfos(typeof(T), true);

            foreach (var item1 in list)
            {

                model = Activator.CreateInstance<T>();

                PropertyInfoAndARAttribute propertyAndAR = null;
                ARFieldAttribute arField = null;
                object value;
                Type NETType;

                fieldValue = ((EntryFieldValue)item1).FieldValues;

                foreach (uint item in fieldValue.Keys)
                {
                    propertyAndAR = properties.FirstOrDefault(o => o.ARAttribute.DatabaseID == item);
                    if (propertyAndAR == null)
                        throw new InvalidCastException(string.Format("valueList cannot cast to model : {0}", typeof(T).FullName),
                            new Exception(string.Format("DatabaseID : {0} in model : {1} cannot be found in valueList",
                                propertyAndAR.ARAttribute.DatabaseID, typeof(T).FullName)));
                    arField = propertyAndAR.ARAttribute;
                    value = fieldValue[arField.DatabaseID];
                    NETType = propertyAndAR.Property.PropertyType;

                    if ((null == value || DBNull.Value == value))
                    {
                        if (NETType.IsGenericType && NETType.GetGenericTypeDefinition() == typeof(Nullable<>)) value = null; //将属性改为Nullable是可行的。
                        if (ARType.SelectionField == arField.DataType) value = Enum.ToObject(NETType, -1);
                        continue;
                    }

                    if (propertyAndAR.Property.PropertyType == typeof(DateTime)) value=((DateTime)value).ToLocalTime();
                    //{
                    //    DateTime time=(DateTime)value;
                    //    DateTime.SpecifyKind(time,DateTimeKind.Utc);
                    //    value=
                    // }

                    propertyAndAR.FastInvoker.Execute(model,
                        new object[] { value });
                }
                model.ClearChange();
                listM.Add(model);
            }
            return listM;
        }


        private string ConvertString(string str)
        {
            if (str == null) return "";
            return Global.CharacterEncoding.GetString(System.Text.Encoding.UTF8.GetBytes(str));
        }

        /// <returns>true : pass; false : block</returns>
        private bool PropertyFilterForChanged(ARBaseForm model, PropertyInfo pi)
        {
            if (model == null)
                throw new ArgumentNullException("model", "model is probably not inherit from ARBaseForm");
            if (pi == null)
                throw new ArgumentNullException("pi");

            return model.ChangedProperties.Contains(pi.Name);
        }


        public ARFieldAttribute GetARAttributeField(PropertyInfo pi, ModelBinderAccessLevel accessLevel)
        {
            throw new NotImplementedException();
        }
    }
}
